﻿// Decompiled with JetBrains decompiler
// Type: Netick.NetworkRpc
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Collections.Generic;

#nullable disable
namespace Netick;

/// <summary>Represents an RPC. Used internally only.</summary>
public class NetworkRpc : ISerializableMessage
{
  internal INetickNetworkScript NetworkBehaviour;
  internal RpcManager Pool;
  public int Id;
  public RpcPeers Target;
  internal bool IsValid = true;
  internal bool IsStatic;
  internal NetworkPlayerId TargetPlayer;
  internal NetworkPlayerId SourcePlayer;
  internal bool IsReliable;
  internal bool IsTargeted;
  public unsafe int* Data;
  public int SizeWords;
  internal Dictionary<NetworkConnection, int> ConnectionToAttempts;

  public int SizeBytes => this.SizeWords * 4;

  internal NetworkRpc(RpcManager pool, int maxPlayers)
  {
    this.Pool = pool;
    this.ConnectionToAttempts = new Dictionary<NetworkConnection, int>(maxPlayers);
  }

  internal unsafe NetworkRpc Init(
    int hashId,
    INetickNetworkScript behaviour,
    bool isReliable,
    RpcPeers target,
    bool isTargeted,
    NetworkPlayerId sourcePlayer,
    NetworkPlayerId targetPlayer,
    int sizeWords)
  {
    this.IsValid = true;
    this.Id = this.Pool.RPC_HashToIndex[hashId];
    this.IsReliable = isReliable;
    this.Target = target;
    this.NetworkBehaviour = behaviour;
    this.SizeWords = sizeWords;
    this.IsStatic = behaviour == null;
    this.SourcePlayer = sourcePlayer;
    this.TargetPlayer = targetPlayer;
    this.IsTargeted = isTargeted;
    this.Data = (int*) this.Pool.RpcAllocator.Allocate((long) sizeWords * 4L);
    return this;
  }

  internal void Recycle()
  {
    this.Id = -1;
    this.IsValid = true;
    if (this.IsStatic)
      this.Pool.RelatedStaticRPCs.Remove(this);
    else
      this.NetworkBehaviour.RelatedRpcs.Remove(this);
    this.ConnectionToAttempts.Clear();
    this.Pool.Push(this);
  }

  void ISerializableMessage.UnregisterConnection(NetworkConnection connection)
  {
    this.ConnectionToAttempts.Remove(connection);
    if (this.ConnectionToAttempts.Count >= 1)
      return;
    this.Recycle();
  }

  void ISerializableMessage.RegisterConnection(NetworkConnection connection, int attempts)
  {
    this.ConnectionToAttempts.Add(connection, attempts);
  }

  void ISerializableMessage.RegisterSent(NetworkConnection connection)
  {
    if (this.ConnectionToAttempts[connection] > 0)
      this.ConnectionToAttempts[connection]--;
    if (this.ConnectionToAttempts[connection] < 1)
      this.ConnectionToAttempts.Remove(connection);
    if (this.ConnectionToAttempts.Count >= 1)
      return;
    this.Recycle();
  }

  int ISerializableMessage.GetAttempts(NetworkConnection connection)
  {
    return this.ConnectionToAttempts.ContainsKey(connection) ? this.ConnectionToAttempts[connection] : 0;
  }

  unsafe void ISerializableMessage.Write(NetworkConnection connection, BitBuffer buffer)
  {
    buffer.AddInt4(this.Id);
    buffer.AddBool(this.IsValid);
    if (!this.IsValid)
      return;
    if (connection.Engine.IsServer)
      buffer.AddInt(this.SourcePlayer.Id);
    if (this.IsTargeted)
      buffer.AddInt(this.TargetPlayer.Id);
    if (!this.IsStatic)
    {
      buffer.AddInt(this.NetworkBehaviour.Entity.RawId);
      buffer.AddInt4(this.NetworkBehaviour.Index);
    }
    for (int index = 0; index < this.SizeWords; ++index)
      buffer.AddInt(this.Data[index]);
  }

  internal static unsafe BufferedRpc ReadRpc(
    RpcManager pool,
    NetworkConnection source,
    BitBuffer buffer,
    int rpcId,
    bool invoke)
  {
    NetickEngine engine = pool.Engine;
    BufferedRpc bufferedRpc = new BufferedRpc();
    bufferedRpc.EntityId = -1;
    bufferedRpc.RpcId = rpcId;
    bufferedRpc.IsValid = buffer.ReadBool();
    if (bufferedRpc.IsValid)
    {
      RpcDefinitionInfo rpcDefinitionInfo1 = new RpcDefinitionInfo();
      RpcDefinitionInfo rpcDefinitionInfo2;
      try
      {
        rpcDefinitionInfo2 = pool.RpcIdToDefinitionInfo[bufferedRpc.RpcId];
      }
      catch (Exception ex)
      {
        throw;
      }
      bufferedRpc.SizeWords = rpcDefinitionInfo2.SizeWords;
      bufferedRpc.Source = !engine.IsClient ? source.PlayerId : NetworkPlayerId.Create(buffer.ReadInt());
      bufferedRpc.Target = new NetworkPlayerId();
      if (rpcDefinitionInfo2.IsTargeted)
        bufferedRpc.Target = NetworkPlayerId.Create(buffer.ReadInt());
      if (!rpcDefinitionInfo2.IsStatic)
      {
        bufferedRpc.EntityId = buffer.ReadInt();
        bufferedRpc.BehId = buffer.ReadInt4();
      }
      try
      {
        bufferedRpc.Data = (int*) pool.RpcAllocator.Allocate((long) bufferedRpc.SizeWords * 4L);
        for (int index = 0; index < bufferedRpc.SizeWords; ++index)
          bufferedRpc.Data[index] = buffer.ReadInt();
        if (engine.IsServer & invoke)
        {
          Entity entity = (Entity) null;
          bool flag = true;
          if (bufferedRpc.EntityId != -1 && !engine.AllEntities.TryGetValue(bufferedRpc.EntityId, out entity))
            flag = false;
          if (flag && rpcDefinitionInfo2.IsTargeted && (bufferedRpc.Target == engine.LocalPlayer.PlayerId || !engine.ContainsPlayer(bufferedRpc.Target)))
            flag = false;
          if (flag)
          {
            if (NetworkRpc.CanRelay(engine, entity, rpcDefinitionInfo2.Source, rpcDefinitionInfo2.Target))
            {
              NetworkRpc relayedRpc = NetworkRpc.InternalCreateRelayedRpc(engine, rpcDefinitionInfo2.IsTargeted, bufferedRpc.Source, bufferedRpc.Target, pool.RPC_IndexToHash[bufferedRpc.RpcId], rpcDefinitionInfo2.IsStatic ? (INetickNetworkScript) null : entity.UserEntity.NetworkScripts[bufferedRpc.BehId], rpcDefinitionInfo2.IsReliable, rpcDefinitionInfo2.Target, rpcDefinitionInfo2.SizeWords);
              for (int index = 0; index < bufferedRpc.SizeWords; ++index)
                relayedRpc.Data[index] = bufferedRpc.Data[index];
            }
          }
        }
      }
      catch (Exception ex)
      {
        pool.RpcAllocator.Free((void*) bufferedRpc.Data, bufferedRpc.SizeWords * 4);
        throw;
      }
    }
    return bufferedRpc;
  }

  internal static unsafe void ReadRpc(
    NetickEngine engine,
    NetworkConnection Connection,
    BitBuffer buffer,
    int rpcId,
    bool invoke)
  {
    BufferedRpc bufferedRpc = NetworkRpc.ReadRpc(engine._rpcManager, Connection, buffer, rpcId, invoke);
    if (!bufferedRpc.IsValid)
      return;
    if (invoke)
      Connection.BufferedRpcs.Add(bufferedRpc);
    else
      engine._rpcManager.RpcAllocator.Free((void*) bufferedRpc.Data, bufferedRpc.SizeWords * 4);
  }

  internal static unsafe void InvokeRpcs(NetickEngine engine, NetworkConnection connection)
  {
    for (int index = 0; index < connection.BufferedRpcs.Count; ++index)
    {
      BufferedRpc bufferedRpc = connection.BufferedRpcs[index];
      engine.CurrentRpcSource = connection;
      engine._rpcManager.CurrentRpcId = bufferedRpc.RpcId;
      if (bufferedRpc.IsValid)
      {
        if (bufferedRpc.EntityId == -1)
        {
          NetworkRpc.InvokeRpc(engine, bufferedRpc.Data, bufferedRpc.RpcId, (Entity) null, bufferedRpc.BehId, bufferedRpc.Source, bufferedRpc.Target);
        }
        else
        {
          Entity entity;
          if (engine.AllEntities.TryGetValue(bufferedRpc.EntityId, out entity))
            NetworkRpc.InvokeRpc(engine, bufferedRpc.Data, bufferedRpc.RpcId, entity, bufferedRpc.BehId, bufferedRpc.Source, bufferedRpc.Target);
        }
      }
      if (bufferedRpc.IsValid)
        engine._rpcManager.RpcAllocator.Free((void*) bufferedRpc.Data, bufferedRpc.SizeWords * 4);
    }
    connection.BufferedRpcs.Clear();
    engine.CurrentRpcSource = (NetworkConnection) null;
    engine._rpcManager.CurrentRpcId = -1;
  }

  internal static unsafe void InvokeRpc(
    NetickEngine engine,
    int* data,
    int rpcId,
    Entity entity,
    int behaviourId,
    NetworkPlayerId source,
    NetworkPlayerId target)
  {
    try
    {
      INetickNetworkScript targetScript = (INetickNetworkScript) null;
      if (entity != null)
        targetScript = entity.UserEntity.NetworkScripts[behaviourId];
      Action<NetickEngine, INetickNetworkScript, RpcContext, NetworkPlayerId, IntPtr> action = engine._rpcManager.RPC_IndexToHandler[rpcId];
      if (action == null)
        throw new BadPacketException($"Rpc with Id {rpcId} was not found.");
      try
      {
        if (target.IsValid && (!target.IsValid || !(target == engine.LocalPlayer.PlayerId)))
          return;
        action(engine, targetScript, new RpcContext(source), target, new IntPtr((void*) data));
      }
      catch (Exception ex)
      {
        if (targetScript != null)
          NetickLogger.LogError((object) ex, (object) targetScript);
        else
          NetickLogger.LogError((object) ex);
      }
    }
    catch (Exception ex)
    {
      NetickLogger.LogError((object) $"{ex.TargetSite.Name}: {ex}");
      if (!entity.Engine.IsServer || !entity.Engine._config.KickClientForBadRpc)
        return;
      if (entity.Engine.IsLoggingEnabled)
        NetickLogger.LogError(entity.Engine, (object) $"Kicked client {entity.Engine.CurrentRpcSource.EndPoint} for bad Rpc - Rpc id {rpcId}");
      entity.Engine.Server.Kick((ServerConnection) entity.Engine.CurrentRpcSource);
    }
  }

  internal static void EnqueueRpc(
    NetickEngine engine,
    NetworkRpc rpc,
    bool isTargetedRpc,
    NetworkPlayerId targetPlayer,
    bool isRelayed)
  {
    if (engine.IsServer)
      NetworkRpc.EnqueueRpcServer(rpc, engine, isTargetedRpc, targetPlayer, isRelayed);
    else
      NetworkRpc.EnqueueRpcClient(rpc, engine._client);
  }

  internal static void EnqueueRpcClient(NetworkRpc rpc, Client client)
  {
    client.ConnectedServer.EnqueueRpc(rpc);
  }

  internal static void EnqueueRpcServer(
    NetworkRpc rpc,
    NetickEngine engine,
    bool isTargetedRpc,
    NetworkPlayerId targetPlayer,
    bool isRelayed)
  {
    List<ServerConnection> connectedClients = engine._server.ConnectedClients;
    bool isStatic = rpc.IsStatic;
    Entity entity = (Entity) null;
    NetworkPlayer networkPlayer = (NetworkPlayer) null;
    if (engine.StartMode == NetickStartMode.SinglePlayer)
    {
      rpc.Recycle();
    }
    else
    {
      if (!isStatic)
      {
        entity = rpc.NetworkBehaviour.Entity;
        networkPlayer = entity.InputSource;
        rpc.NetworkBehaviour.RelatedRpcs.Add(rpc);
      }
      else
        engine._rpcManager.RelatedStaticRPCs.Add(rpc);
      int id = rpc.Id;
      if (isTargetedRpc && targetPlayer.IsValid)
      {
        if (engine.ContainsPlayer(targetPlayer))
        {
          if (engine.GetPlayerById(targetPlayer) is ServerConnection playerById)
          {
            if (playerById.IsReady)
            {
              playerById.EnqueueRpc(rpc);
              return;
            }
            rpc.Recycle();
            return;
          }
        }
        else
        {
          rpc.Recycle();
          return;
        }
      }
      if (rpc.Target == RpcPeers.Owner)
        rpc.Recycle();
      else if (entity != null && rpc.Target == RpcPeers.InputSource)
      {
        if (entity.InputSource != null)
        {
          if (!(entity.InputSource is ServerConnection inputSource))
            return;
          inputSource.EnqueueRpc(rpc);
        }
        else
          rpc.Recycle();
      }
      else if (connectedClients.Count < 1)
        rpc.Recycle();
      else if (rpc.Target == RpcPeers.Proxies)
      {
        if (isRelayed)
        {
          for (int index = 0; index < connectedClients.Count; ++index)
          {
            if (engine.CurrentRpcSource != connectedClients[index] && engine.CurrentRpcSource != networkPlayer)
              connectedClients[index].EnqueueRpc(rpc);
          }
        }
        else
        {
          for (int index = 0; index < connectedClients.Count; ++index)
          {
            if (connectedClients[index] != networkPlayer)
              connectedClients[index].EnqueueRpc(rpc);
          }
        }
      }
      else if (isRelayed)
      {
        for (int index = 0; index < connectedClients.Count; ++index)
        {
          if (engine.CurrentRpcSource != connectedClients[index])
            connectedClients[index].EnqueueRpc(rpc);
        }
      }
      else
      {
        for (int index = 0; index < connectedClients.Count; ++index)
          connectedClients[index].EnqueueRpc(rpc);
      }
    }
  }

  /// <summary>Internal method. Never call this.</summary>
  public static bool InternalIsResim(NetickEngine engine) => engine.IsResimulating;

  /// <summary>Internal method. Never call this.</summary>
  public static NetworkRpc InternalCreateRpc(
    NetickEngine engine,
    bool isTargeted,
    NetworkPlayerId targetPlayer,
    int id,
    INetickNetworkScript behaviour,
    bool isReliable,
    RpcPeers target,
    int wordSize)
  {
    NetworkRpc rpc = engine._rpcManager.Get(id, behaviour, isReliable, target, isTargeted, engine.LocalPlayer.PlayerId, targetPlayer, wordSize);
    NetworkRpc.EnqueueRpc(engine, rpc, isTargeted, targetPlayer, false);
    return rpc;
  }

  public static NetworkRpc InternalCreateRelayedRpc(
    NetickEngine engine,
    bool isTargeted,
    NetworkPlayerId sourcePlayer,
    NetworkPlayerId targetPlayer,
    int id,
    INetickNetworkScript behaviour,
    bool isReliable,
    RpcPeers target,
    int wordSize)
  {
    NetworkRpc rpc = engine._rpcManager.Get(id, behaviour, isReliable, target, isTargeted, sourcePlayer, targetPlayer, wordSize);
    NetworkRpc.EnqueueRpc(engine, rpc, isTargeted, targetPlayer, true);
    return rpc;
  }

  /// <summary>Internal method. Never call this.</summary>
  public static bool InternalCanSend(
    NetickEngine engine,
    Entity entity,
    RpcPeers source,
    RpcPeers target,
    int id)
  {
    int currentRpcId = engine._rpcManager.CurrentRpcId;
    if (entity != null && (entity.RawId == -1 || target == RpcPeers.InputSource && entity.IsInputSource) || currentRpcId != -1 || target == RpcPeers.Owner && engine.IsServer)
      return false;
    if (source == RpcPeers.Everyone)
      return true;
    if (entity != null)
    {
      if (source == RpcPeers.InputSource)
        return entity.IsInputSource;
      if (source == RpcPeers.Proxies)
        return !entity.IsInputSource;
    }
    return source == RpcPeers.Owner && engine.IsServer;
  }

  internal static bool CanRelay(
    NetickEngine engine,
    Entity entity,
    RpcPeers source,
    RpcPeers target)
  {
    if (entity != null && (entity.RawId == -1 || target == RpcPeers.InputSource && entity.IsInputSource) || !NetworkRpc.IsSourceCorrect(source, engine, entity))
      return false;
    if (target != RpcPeers.Owner)
      return true;
    if (target != RpcPeers.Owner)
      return false;
    int num = engine.IsServer ? 1 : 0;
    return false;
  }

  public static bool InternalCanInvoke(
    NetickEngine engine,
    Entity obj,
    RpcPeers source,
    RpcPeers target,
    bool localInvoke,
    int rpcId,
    bool isTargeted,
    NetworkPlayerId targetPlayer)
  {
    RpcContext rpcContext = new RpcContext();
    return NetworkRpc.InternalCanInvokeWithRID(engine, obj, source, target, localInvoke, rpcId, isTargeted, targetPlayer, ref rpcContext);
  }

  /// <summary>Internal method. Never call this.</summary>
  public static bool InternalCanInvokeWithRID(
    NetickEngine engine,
    Entity obj,
    RpcPeers source,
    RpcPeers target,
    bool localInvoke,
    int rpcHash,
    bool isTargeted,
    NetworkPlayerId targetPlayer,
    ref RpcContext rpcContext)
  {
    int currentRpcId = engine._rpcManager.CurrentRpcId;
    engine._rpcManager.CurrentRpcId = -1;
    int num = engine._rpcManager.RPC_HashToIndex[rpcHash];
    if (currentRpcId == -1)
      rpcContext = new RpcContext(engine.LocalPlayer.PlayerId);
    if (currentRpcId != num)
      return ((!(targetPlayer == engine.LocalPlayer.PlayerId) ? 0 : (target == RpcPeers.Everyone ? 1 : 0)) & (isTargeted ? 1 : 0)) != 0 || localInvoke && target == RpcPeers.Everyone && !isTargeted || engine.IsServer && target == RpcPeers.Owner || obj != null && (obj.IsInputSource && target == RpcPeers.InputSource || obj.IsProxy && target == RpcPeers.Proxies) || localInvoke;
    if (engine.IsServer && !NetworkRpc.IsSourceCorrect(source, engine, obj))
      return false;
    if (target == RpcPeers.Everyone)
      return true;
    if (target == RpcPeers.Owner)
      return engine.IsServer;
    if (obj != null)
    {
      if (target == RpcPeers.InputSource)
        return obj.IsInputSource;
      if (target == RpcPeers.Proxies)
        return !obj.IsInputSource;
    }
    return false;
  }

  private static bool IsSourceCorrect(RpcPeers source, NetickEngine engine, Entity entity)
  {
    if (entity != null)
    {
      NetworkConnection currentRpcSource = engine.CurrentRpcSource;
      if (source == RpcPeers.InputSource && entity.InputSource != null && entity.InputSource is NetworkConnection && entity.InputSource != currentRpcSource || source == RpcPeers.Proxies && entity.InputSource == currentRpcSource && engine.IsClient)
        return false;
    }
    return source != RpcPeers.Owner;
  }
}
